﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Microsoft.SharePoint;
using Smart.Common.Entities;

namespace Smart.Common
{
	public static class EntityExts
	{
		#region Entity converter
		/// <summary>
		/// ItemConverter for Entities
		/// </summary>
		public static TItemType EntityItemConverter<TItemType, TCollectionType>(SPListItem item, TCollectionType collection)
			where TItemType : Entity, new()
			where TCollectionType : EntityList<TItemType>
		{
			var ei = collection.Service.DetectEntityInfo(item);

			var it = typeof(TItemType);

			if (ei != null && ei.Type.IsSubclassOf(it))
				it = ei.Type;

			var entity = collection.Service.CreateEntity(it, collection);

			collection.Service.LoadEntity(entity, item);

			return entity;
		}

		/// <summary>
		/// Collection creator for Entities
		/// </summary>
		public static EntityList<TEntityType> EntityCollectionCreator<TEntityType>(MetaList meta, SPList list)
			where TEntityType : Entity, new()
		{
			return new EntityList<TEntityType>(meta, new WebListService<TEntityType>(list, WebEntityMapper.Current, meta));
		}

		/// <summary>
		/// Convert Items to new collection and type
		/// </summary>
		public static TCollectionType Convert<TItemType, TCollectionType>(this SPListItemCollection items, MetaList meta, SPList list,
																		  Func<SPListItem, TCollectionType, TItemType> itemConverter,
																		  Func<MetaList, SPList, TCollectionType> collectionCreator)
			where TCollectionType : class, IList<TItemType>
		{
			var res = collectionCreator(meta, list);

			if (items != null)
			{
				foreach (SPListItem item in items)
				{
					res.Add(itemConverter(item, res));
				}
			}

			return res;
		}

		/// <summary>
		/// Convert Items to new collection and type
		/// </summary>
		public static TCollectionType Convert<TItemType, TCollectionType>(this IEnumerable<SPListItem> items, MetaList meta, SPList list,
																		  Func<SPListItem, TCollectionType, TItemType> itemConverter,
																		  Func<MetaList, SPList, TCollectionType> collectionCreator)
			where TCollectionType : class, IList<TItemType>
		{
			var res = collectionCreator(meta, list);

			if (items != null)
			{
				foreach (var item in items)
				{
					res.Add(itemConverter(item, res));
				}
			}

			return res;
		}

		/// <summary>
		/// Create entity
		/// </summary>
		public static TEntityType CreateEntity<TEntityType>(this SPList list)
			where TEntityType : Entity, new()
		{
			return CreateEntity<TEntityType>(list, MetaCtx.MetaList(list.ParentWeb, list.ID));
		}

		/// <summary>
		/// Create entity
		/// </summary>
		public static TEntityType CreateEntity<TEntityType>(this SPList list, MetaList meta)
			where TEntityType : Entity, new()
		{
			if (meta == null)
				throw new ArgumentNullException("meta");

			var coll = EntityCollectionCreator<TEntityType>(meta, list);

			return coll.Create();
		}

		/// <summary>
		/// Convert Item to Entity
		/// </summary>
		public static WebEntity ToEntity(this SPListItem item)
		{
			if (item == null)
				return null;

			var list = item.ParentList;

			return ToEntity(item, MetaCtx.MetaList(list.ParentWeb, list.ID));
		}

		/// <summary>
		/// Convert Item to Entity
		/// </summary>
		public static WebEntity ToEntity(this SPListItem item, MetaList meta)
		{
			if (item == null)
				return null;

			if (meta == null)
				throw new ArgumentNullException("meta");

			var list = item.ParentList;

			var coll = EntityCollectionCreator<WebEntity>(meta, list);

			var ei = coll.Service.DetectEntityInfo(item);

			var entity = coll.Service.CreateEntity(ei.Type, coll);

			coll.Service.LoadEntity(entity, item);

			coll.Add(entity);

			return entity;
		}

		/// <summary>
		/// Convert Item to Entity
		/// </summary>
		public static TEntityType ToEntity<TEntityType>(this SPListItem item)
			where TEntityType : Entity, new()
		{
			if (item == null)
				return null;

			var list = item.ParentList;

			return ToEntity<TEntityType>(item, MetaCtx.MetaList(list.ParentWeb, list.ID));
		}

		/// <summary>
		/// Convert Item to Entity
		/// </summary>
		public static TEntityType ToEntity<TEntityType>(this SPListItem item, MetaList meta)
			where TEntityType : Entity, new()
		{
			if (item == null)
				return null;

			if (meta == null)
				throw new ArgumentNullException("meta");

			var list = item.ParentList;

			var coll = EntityCollectionCreator<TEntityType>(meta, list);

			coll.Add(EntityItemConverter<TEntityType, EntityList<TEntityType>>(item, coll));

			return coll[0];
		}

		/// <summary>
		/// Convert Items to EntityList
		/// </summary>
		public static EntityList<TEntityType> ToEntities<TEntityType>(this IEnumerable<SPListItem> items, SPList list) where TEntityType : Entity, new()
		{
			if (items == null)
				return null;

			if (list == null)
				throw new ArgumentNullException("list");

			return ToEntities<TEntityType>(items, list, MetaCtx.MetaList(list.ParentWeb, list.ID));
		}

		/// <summary>
		/// Convert Items to EntityList
		/// </summary>
		public static EntityList<TEntityType> ToEntities<TEntityType>(this IEnumerable<SPListItem> items, SPList list, MetaList meta)
			where TEntityType : Entity, new()
		{
			if (items == null)
				return null;

			if (list == null)
				throw new ArgumentNullException("list");

			if (meta == null)
				throw new ArgumentNullException("meta");

			return Convert<TEntityType, EntityList<TEntityType>>(items, meta, list, EntityItemConverter<TEntityType, EntityList<TEntityType>>,
																 EntityCollectionCreator<TEntityType>);
		}

		/// <summary>
		/// Convert Items to EntityList
		/// </summary>
		public static EntityList<TEntityType> ToEntities<TEntityType>(this SPListItemCollection items) where TEntityType : Entity, new()
		{
			if (items == null)
				return null;

			var list = items.List;

			return ToEntities<TEntityType>(items, MetaCtx.MetaList(list.ParentWeb, list.ID));
		}

		/// <summary>
		/// Convert Items to EntityList
		/// </summary>
		public static EntityList<TEntityType> ToEntities<TEntityType>(this SPListItemCollection items, MetaList meta) where TEntityType : Entity, new()
		{
			return Convert<TEntityType, EntityList<TEntityType>>(items, meta, items.List, EntityItemConverter<TEntityType, EntityList<TEntityType>>, EntityCollectionCreator<TEntityType>);
		}

		/// <summary>
		/// Convert DataView to Entities
		/// </summary>
		public static EntityList<TEntityType> ToEntities<TEntityType>(this DataView items, MetaFields meta) where TEntityType : Entity, new()
		{
			var coll = new EntityList<TEntityType>(meta, new WebListService<TEntityType>(null, DataEntityMapper.Current, meta as MetaList));

			foreach (var item in items)
			{
				var ei = coll.Service.DetectEntityInfo(item);

				var it = typeof(TEntityType);

				if (ei != null && ei.Type.IsSubclassOf(it))
					it = ei.Type;

				var entity = coll.Service.CreateEntity(it, coll);

				coll.Service.LoadEntity(entity, item);

				coll.Add(entity);
			}

			return coll;
		}
		#endregion

		#region Entity Value helpers
		/// <summary>
		/// Ensure specified values exists
		/// </summary>
		/// <returns>true, if value changed</returns>
		public static bool Ensure(this UserValueCollection collection, params SPUser[] values)
		{
			return collection.Ensure(values.Select(value => value != null ? new Identifier(value.ID) : Identifier.Empty).ToArray());
		}

		/// <summary>
		/// Set values
		/// </summary>
		public static void Set(this UserValueCollection collection, params SPUser[] values)
		{
			collection.Set(values.Select(value => value != null ? new Identifier(value.ID) : Identifier.Empty).ToArray());
		}

		/// <summary>
		/// Ensure specified values exists
		/// </summary>
		/// <returns>true, if value changed</returns>
		public static bool Ensure(this LookupValueCollection collection, params SPListItem[] values)
		{
			return collection.Ensure(values.Select(value => value != null ? new Identifier(value.ID) : Identifier.Empty).ToArray());
		}

		/// <summary>
		/// Set values
		/// </summary>
		public static void Set(this LookupValueCollection collection, params SPListItem[] values)
		{
			collection.Set(values.Select(value => value != null ? new Identifier(value.ID) : Identifier.Empty).ToArray());
		}

		/// <summary>
		/// Return url for userdisp.aspx form
		/// </summary>
		[Obsolete("No hard code allowed!")]
		public static string GetUserUrl(this UserValue user)
		{
			var web = SPH.GetRootWeb();

			if (user == null || web == null)
				throw new ArgumentException();

			return String.Format("{0}/_layouts/userdisp.aspx?ID={1}", web.Url, user.ID);
		}

		/// <summary>
		/// Return emails for user or group
		/// </summary>
		public static string GetEmails(this UserValue user, SPWeb web)
		{
			if (user == null || user.IsEmpty)
				return string.Empty;

			return web.GetEmails(user.ID.Int);
		}

		/// <summary>
		/// Return emails for users or groups
		/// </summary>
		public static string GetEmails(this UserValueCollection users, SPWeb web)
		{
			return
				users.Select(value => value.GetEmails(web)).Where(s => !s.IsNullOrEmpty()).CastToString(";");
		}

		/// <summary>
		/// Return HTML A for userdisp.aspx form
		/// </summary>
		[Obsolete("No display code allowed!")]
		public static string GetUserLink(this UserValue user)
		{
			return String.Format("<a href=\"{0}\">{1}</a>", user.GetUserUrl(), user.Title);
		}

		/// <summary>
		/// Return HTML A for link
		/// </summary>
		[Obsolete("No display code allowed!")]
		public static string GetLink(this LinkValue link)
		{
			if (ReferenceEquals(link, null) || link.Url.IsNullOrEmpty() || link.Title.IsNullOrEmpty())
				return string.Empty;

			return string.Format(@"<a href=""{0}"">{1}</a>", link.Url, link.Title);
		}
		#endregion
	}
}
